package com.onlyoffice.demo.findproblem.service.impl;

import cn.hutool.core.lang.UUID;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.onlyoffice.demo.findproblem.domain.ClickCheckRequest;
import com.onlyoffice.demo.findproblem.domain.ClickCheckResponse;
import com.onlyoffice.demo.findproblem.domain.ErrorSpot;
import com.onlyoffice.demo.findproblem.domain.GameLevel;
import com.onlyoffice.demo.findproblem.mapper.ErrorSpotMapper;
import com.onlyoffice.demo.findproblem.mapper.GameLevelMapper;
import com.onlyoffice.demo.findproblem.service.GameService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class GameServiceImpl implements GameService {

    private final GameLevelMapper gameLevelMapper;
    private final ErrorSpotMapper errorSpotMapper;

    @Value("${onlyoffice.uploadPath}")
    private String uploadPath;

    @Override
    public List<GameLevel> getAllLevels() {
        return gameLevelMapper.selectByStatus(1);
    }

    @Override
    public IPage<GameLevel> getLevelsByPage(Long current, Long size, Integer difficulty) {
        Page<GameLevel> page = new Page<>(current, size);

        LambdaQueryWrapper<GameLevel> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(GameLevel::getStatus, 1)
                .eq(difficulty != null, GameLevel::getDifficulty, difficulty);

        return gameLevelMapper.selectPage(page, queryWrapper);
    }

    @Override
    public GameLevel getLevelForPlay(Long id) {
        GameLevel level = gameLevelMapper.selectById(id);
        if (level == null || level.getStatus() != 1) {
            throw new RuntimeException("关卡不存在或已禁用");
        }

        GameLevel dto = convertToDTO(level);
        // 游戏时不返回错误点坐标，只返回基本信息
        dto.setErrorSpots(null);
        return dto;
    }

    @Override
    public GameLevel getLevelWithSpots(Long id) {
        GameLevel level = gameLevelMapper.selectById(id);
        if (level == null) {
            throw new RuntimeException("关卡不存在");
        }

        // 查询错误点
        List<ErrorSpot> errorSpots = errorSpotMapper.selectByLevelId(id);
        level.setErrorSpots(errorSpots);

        return level;
    }

    @Override
    public ClickCheckResponse checkClick(ClickCheckRequest request) {
        // 优化：先查询点击位置附近的错误点，减少计算量
        float searchRadius = 50f; // 搜索半径
        float minX = Math.max(0, request.getX() - searchRadius);
        float maxX = Math.min(100, request.getX() + searchRadius);
        float minY = Math.max(0, request.getY() - searchRadius);
        float maxY = Math.min(100, request.getY() + searchRadius);

        List<ErrorSpot> nearbySpots = errorSpotMapper.selectByCoordinateRange(
                request.getLevelId(), minX, maxX, minY, maxY);

        // 精确计算距离
        for (ErrorSpot spot : nearbySpots) {
            double distance = calculateDistance(
                    request.getX(), request.getY(),
                    spot.getXCoordinate(), spot.getYCoordinate()
            );

            if (distance <= spot.getRadius()) {
                return ClickCheckResponse.builder()
                        .hit(true)
                        .spotId(spot.getId())
                        .message("找到了！" + spot.getDescription())
                        .score(100)
                        .build();
            }
        }

        return ClickCheckResponse.builder()
                .hit(false)
                .message("没找到错误点")
                .score(0)
                .build();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public GameLevel createLevel(GameLevel levelDTO) {
        // 创建关卡
        GameLevel level = new GameLevel();
        BeanUtils.copyProperties(levelDTO, level, "id", "errorSpots");
        level.setStatus(1);

        int result = gameLevelMapper.insert(level);
        if (result <= 0) {
            throw new RuntimeException("创建关卡失败");
        }

        // 批量创建错误点
        if (levelDTO.getErrorSpots() != null && !levelDTO.getErrorSpots().isEmpty()) {
            List<ErrorSpot> errorSpots = levelDTO.getErrorSpots().stream()
                    .map(spotDTO -> {
                        ErrorSpot spot = new ErrorSpot();
                        BeanUtils.copyProperties(spotDTO, spot, "id");
                        spot.setLevelId(level.getId());
                        if (spot.getRadius() == null) {
                            spot.setRadius(30f);
                        }
                        return spot;
                    })
                    .collect(Collectors.toList());

            int batchResult = errorSpotMapper.batchInsert(errorSpots);
            if (batchResult != errorSpots.size()) {
                throw new RuntimeException("创建错误点失败");
            }
        }

        return level;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public GameLevel updateLevel(Long id, GameLevel levelDTO) {
        GameLevel existingLevel = gameLevelMapper.selectById(id);
        if (existingLevel == null) {
            throw new RuntimeException("关卡不存在");
        }

        // 更新关卡基本信息
        BeanUtils.copyProperties(levelDTO, existingLevel, "id", "createdTime", "errorSpots");
        int result = gameLevelMapper.updateById(existingLevel);
        if (result <= 0) {
            throw new RuntimeException("更新关卡失败");
        }

        // 更新错误点：先删除旧的，再插入新的
        if (levelDTO.getErrorSpots() != null) {
            // 删除旧的错误点
            LambdaQueryWrapper<ErrorSpot> deleteWrapper = new LambdaQueryWrapper<>();
            deleteWrapper.eq(ErrorSpot::getLevelId, id);
            errorSpotMapper.delete(deleteWrapper);

            // 插入新的错误点
            if (!levelDTO.getErrorSpots().isEmpty()) {
                List<ErrorSpot> errorSpots = levelDTO.getErrorSpots().stream()
                        .map(spotDTO -> {
                            ErrorSpot spot = new ErrorSpot();
                            BeanUtils.copyProperties(spotDTO, spot, "id");
                            spot.setLevelId(id);
                            if (spot.getRadius() == null) {
                                spot.setRadius(30f);
                            }
                            return spot;
                        })
                        .collect(Collectors.toList());

                errorSpotMapper.batchInsert(errorSpots);
            }
        }

        return existingLevel;
    }

    @Override
    public boolean deleteLevel(Long id) {
        // 使用逻辑删除
        GameLevel level = gameLevelMapper.selectById(id);
        if (level == null) {
            throw new RuntimeException("关卡不存在");
        }

        // 删除关卡（逻辑删除）
        int result = gameLevelMapper.deleteById(id);

        // 删除关联的错误点（逻辑删除）
        if (result > 0) {
            LambdaQueryWrapper<ErrorSpot> deleteWrapper = new LambdaQueryWrapper<>();
            deleteWrapper.eq(ErrorSpot::getLevelId, id);
            errorSpotMapper.delete(deleteWrapper);
        }

        return result > 0;
    }

    @Override
    public String uploadImage(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new RuntimeException("请选择要上传的文件");
        }

        // 验证文件类型
        String contentType = file.getContentType();
        if (!isImageFile(contentType)) {
            throw new RuntimeException("只支持图片文件格式");
        }

        // 验证文件大小（10MB）
        if (file.getSize() > 10 * 1024 * 1024) {
            throw new RuntimeException("文件大小不能超过10MB");
        }

        try {
            String originalFilename = file.getOriginalFilename();
            String fileExtension = getFileExtension(originalFilename);
            String fileName = UUID.randomUUID().toString() + "." + fileExtension;

            // 创建上传目录
            File uploadDir = new File(uploadPath);
            if (!uploadDir.exists()) {
                uploadDir.mkdirs();
            }

            // 保存文件
            File targetFile = new File(uploadDir, fileName);
            file.transferTo(targetFile);

            // 返回访问URL
            return "/resources/" + fileName;

        } catch (IOException e) {
            log.error("文件上传失败", e);
            throw new RuntimeException("文件上传失败");
        }
    }

    // 私有方法
    private double calculateDistance(float x1, float y1, float x2, float y2) {
        return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
    }

    private boolean isImageFile(String contentType) {
        return contentType != null && (
                contentType.startsWith("image/jpeg") ||
                        contentType.startsWith("image/png") ||
                        contentType.startsWith("image/gif") ||
                        contentType.startsWith("image/webp")
        );
    }

    private String getFileExtension(String filename) {
        if (filename == null || !filename.contains(".")) {
            return "jpg";
        }
        return filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
    }

    private GameLevel convertToDTO(GameLevel level) {
        GameLevel dto = new GameLevel();
        BeanUtils.copyProperties(level, dto);
        return dto;
    }
}